﻿////////////////////////////////////////////////////////////////////////////////////
//
//   ParseDTD.cpp
//   Description: parse dtd file into std::list<CElement>
//   Revision History: 03/25/2020 created by Zhang Hong
//
////////////////////////////////////////////////////////////////////////////////////

#ifndef __GNUC__
#pragma execution_character_set("utf-8")
#endif

#include "ParseDTD.h"
#include <assert.h> 

bool CElement::isSubElement(const QString &elmName)
{
	for (auto &sub : subElmList)
	{
		if (sub == elmName) return true;
	}
	return false;
}


CElement *TopElement(std::list<CElement> &elmList)
{
	for (auto &elm : elmList)
	{
		if (elm.parentElmList.size() == 0) return &elm;
	}
	assert(0);
	return NULL;
}

CElement *FindElement(std::list<CElement> &elmList, const QString &name)
{
	for (auto &elm : elmList)
	{
		if (elm.name == name) return &elm;
	}
	return NULL;
}

const CElement *FindElement(const std::list<CElement> &elmList, const QString &name)
{
	for (auto &elm : elmList)
	{
		if (elm.name == name) return &elm;
	}
	return NULL;
}

int ParseDTD(const QString &dtd, std::list<CElement> &elmList, QTreeWidget *treeWidget)
{
	elmList.clear();
	treeWidget->clear();

	//分离单元名称和子单元列表字符串：1--{ $Project }，2--(Node,Flow*)
	QString sRegElement = "<!ELEMENT\\s+(.*?)\\s+(.*?)\\s*>";	
	QRegularExpression reElm(sRegElement);
	if (!reElm.isValid()) return 1;

	//获得单元名称和属性列表（可能多行）：1--{ $Project }，2--name CDATA ""\r\n alias CDATA ""\r\n ...
	QString sRegAttList = "<!ATTLIST\\s+(.*?)[\\s\\r\\n]+([\\s\\S]*?)>";
	QRegularExpression reAttList(sRegAttList);
	if (!reAttList.isValid()) return 2;

	//在一行中单元解析属性
	QString sRegAtt = "[\\t\\s]*(.*?)\\s+.*?\\s+(?:\"(.*?)\"|#REQUIRED)";
	QRegularExpression reAtt(sRegAtt);
	if (!reAtt.isValid()) return 3;

	//nVars = rx.captureCount() + 1;
	QRegularExpressionMatchIterator iterElm = reElm.globalMatch(dtd);

	while (iterElm.hasNext())
	{
		QRegularExpressionMatch match = iterElm.next();

		CElement elm;
		elm.name = match.captured(1);


		//解析子单元
		QString sPara = match.captured(2);
		if (sPara != "EMPTY")
		{
			sPara.replace(QRegExp("[\\s\\(\\)\\*\\+\\?]"), "");
			elm.subElmList = sPara.split(QRegExp("[,|]"));
		}

		elmList.push_back(elm);
	}

	//记录父单元
	for (auto &elm : elmList)
	{
		for (auto &sub : elm.subElmList)
		{
			CElement *subElm = FindElement(elmList, sub);
			if (!subElm) continue;

			int k = subElm->parentElmList.indexOf(elm.name);
			if (k != -1)
				subElm->parentElmList.push_back(elm.name);
		}
	}

	QRegularExpressionMatchIterator iterAttLiat = reAttList.globalMatch(dtd);
	while (iterAttLiat.hasNext())
	{
		QRegularExpressionMatch match = iterAttLiat.next();

		QString elmName;
		elmName = match.captured(1);
		CElement *pElm = NULL;
		for (auto &elm : elmList)
		{
			if (elm.name == elmName)
			{
				pElm = &elm;
				break;
			}
		}
		assert(pElm);
		if (!pElm)
		{
			continue;
		}

		//解析单元属性
		QString sAttr = match.captured(2);
		sAttr.replace(QRegExp("\t"), " ");
		QStringList lines = sAttr.split(QRegExp("[\\r\\n]+"));

		for (auto &line : lines)
		{
			QRegularExpressionMatch matchAttr = reAtt.match(line);
			int n = matchAttr.lastCapturedIndex();
			if (n < 1) continue;

			Attrib attr;
			attr.name = matchAttr.captured(1);

			if (n>=2)
				attr.default = matchAttr.captured(2);
			if (attr.default == "#REQUIRED") 
				attr.default = "";

			pElm->propList.push_back(attr);
		}
	}

	if (treeWidget)
	{
		//获取treeWidget顶层单元
		QTreeWidgetItem *pTopItem = treeWidget->invisibleRootItem();

		for (auto &elm : elmList)
		{
			//插入treeWidget顶层单元
			QStringList qlist;
			qlist << elm.name;
			QTreeWidgetItem *pItem = new QTreeWidgetItem(qlist);
			pTopItem->addChild(pItem);

			//插入子单元
			for (auto &s1 : elm.subElmList)
			{
				qlist.clear();
				qlist << s1;
				QTreeWidgetItem *pItem1 = new QTreeWidgetItem(qlist);
				pItem->addChild(pItem1);
			}

			//插入属性
			qlist.clear();
			qlist << "属性";
			QTreeWidgetItem *pProp = new QTreeWidgetItem(qlist);
			pItem->addChild(pProp);
			for (auto &prop : elm.propList)
			{
				qlist.clear();
				qlist << prop.name + "=" + prop.default;
				QTreeWidgetItem *pProp1 = new QTreeWidgetItem(qlist);
				pProp->addChild(pProp1);
			}

		}
	}

	return 0;
}
